Skip to content

Add API to link/unlink provider info to/from user account. #383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

nrsim
Copy link

@nrsim nrsim commented Jan 13, 2020

No description provided.

@nrsim nrsim requested a review from rsgowman January 13, 2020 18:32
@@ -532,7 +532,8 @@ def create_user(self, uid=None, display_name=None, email=None, phone_number=None

def update_user(self, uid, display_name=None, email=None, phone_number=None,
photo_url=None, password=None, disabled=None, email_verified=None,
valid_since=None, custom_claims=None):
valid_since=None, custom_claims=None, link_provider=None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The names have changed since starting this (and may again; feel free to defer fixing this until the name's are finalized). But as of now:

  • s/link_provider/provider_to_link/
  • s/delete_provider_ids/providers_to_delete/

@@ -541,6 +542,8 @@ def update_user(self, uid, display_name=None, email=None, phone_number=None,
'validSince': _auth_utils.validate_timestamp(valid_since, 'valid_since'),
'emailVerified': bool(email_verified) if email_verified is not None else None,
'disableUser': bool(disabled) if disabled is not None else None,
'linkProviderUserInfo': link_provider.to_dict() if link_provider is not None else None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth validating this parameter? _auth_utils.validate_user_provider() or similar?

@@ -388,6 +388,8 @@ def update_user(uid, **kwargs):
user account (optional). To remove all custom claims, pass ``auth.DELETE_ATTRIBUTE``.
valid_since: An integer signifying the seconds since the epoch. This field is set by
``revoke_refresh_tokens`` and it is discouraged to set this field directly.
link_provider: User's provider info to be linked to the user account.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(names)

@@ -388,6 +388,8 @@ def update_user(uid, **kwargs):
user account (optional). To remove all custom claims, pass ``auth.DELETE_ATTRIBUTE``.
valid_since: An integer signifying the seconds since the epoch. This field is set by
``revoke_refresh_tokens`` and it is discouraged to set this field directly.
link_provider: User's provider info to be linked to the user account.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should indicate the type here. It's especially important in python, since (absent type annotations) it can be non-trivial to figure that out for the user. In this case, maybe "A UserProvider instance that contains the user's provider info to be linked to the user account." ?

user = auth.update_user(
new_user.uid,
link_provider=auth.UserProvider(
uid='test', provider_id='google.com', email='[email protected]',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution: I think that a single provider_uid can only be linked to a single account. So by linking provideruid='test' to this account, unless the account is deleted, the second (and all subsequent) time you run this test, it'll fail. That's mostly ok here though, since the way the new user is being setup, it'll automatically delete itself, even if the test fails. But you could imagine the CI system running kill -9 (or hard power-off, or ...) right in the middle of this test. Highly unlikely... but possible.

A few options:
a) Don't worry about it; it's pretty improbable, and we can fix by hand if we run into it.
b) Instead of using a hardcoded uid ("test") create a random one. (Which makes it another order of magnitude less likely to bite us, even if it doesn't quite eliminate the possibility altogether.)
c) At the start of the test, find the user with provider_id="google.com" and provider_uid="test". Either delete or unlink that user if it exists. (But still delete the user at the end of the test too.)

I'd prefer either b or c.

uid='test', provider_id='google.com', email='[email protected]',
display_name='Test Name', photo_url='https://test.com/user.png'))
assert user.uid == new_user.uid
assert len(user.provider_data) == 3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no worse than what was there before. But consider validating the contents of this list instead. Validating the entire instance might be overkill, but how about:

assert sorted(map(lambda user_info: user_info.provider_id, user.provider_data)) == sorted(["email", "phone", "google.com"])

(untested, and probably too terse. A helper would likely make it more readable.)

delete_provider_ids=['google.com'])
assert user.uid == new_user.uid
assert user.phone_number is None
assert len(user.provider_data) == 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you make a change above, do so here (and below) as well.

assert user.uid == new_user.uid
assert user.phone_number is None
assert len(user.provider_data) == 1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new requirement is that this API should also work with non-federated providers. (It's not something we're going to actually recommend, since there's more direct ways to do that.) Unfortunately, the backend for this API doesn't support non-federated providers, so we're going to have to fake that a bit.

Concretely, you've used update_user() to link to the 'google.com' provider. Could you also add calls for phone, email, oauth and saml? (Not necessarily in this exact test.) It'll require you to also change the implementation.

If this looks like it'll cause this PR to balloon in size, then let's talk first and see if we can come up with other options.

'photoUrl': 'https://test.com/user.png'
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding "failure" tests.
a) empty delete_providers_ids list
b) invalid provider_id. (I'm not sure we can tell if a provider_id is valid or not... other than checking for None or empty string.)
c) invalid provider_uid. (Same; just None/empty checks.)

These should all cause failures before hitting the network, so are easy to check from a unit test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants